/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package solitaireofdoom.solitaire;

import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.awt.image.*;
import solitaireofdoom.klondike.KlondikeDonePile;
import solitaireofdoom.klondike.KlondikePlayPile;
import solitaireofdoom.klondike.KlondikeSuitPile;
import solitaireofdoom.freecell.FreecellCellPile;
import solitaireofdoom.freecell.FreecellFoundationPile;
import solitaireofdoom.freecell.FreecellTableuPile;

/**
 * The CardCanvas class displays a single card and has basic drag capabilities, although it is not displayed visually it is quite extensive since we are using the Drag 'n Drop packages supplied by java (or sun). The Drag 'n Drop packages were designed for files and text being dragged between applications but we found that it was easily implemented for our purposes.
 * 
 * @author Administrator
 */
public class CardCanvas extends java.awt.Canvas
{
    public Pile pile;

    protected Card card;
    protected BufferedImage cards;

    private boolean hintVisible = false;

    protected DragGestureListener dragGestureListener;
    protected DragSource dragSource;
    protected DragSourceListener dragSourceListener;
    protected int dragAction = DnDConstants.ACTION_MOVE;

    public CardCanvas()
    {
        super();
    }

    public CardCanvas(BufferedImage cards)
    {
        super();

        this.cards = cards;

        this.setSize(Card.CardWidth, Card.CardHeight);

        //this.setIgnoreRepaint(true);

        CardCanvasMouseHandler mouseHandler = new CardCanvasMouseHandler();
        this.addMouseListener(mouseHandler);

        this.dragSource = DragSource.getDefaultDragSource();
        this.dragGestureListener = new CardCanvasDragGestureListener();
        this.dragSourceListener = new CardCanvasDragSourceListener();

        this.dragSource.createDefaultDragGestureRecognizer(this, this.dragAction, this.dragGestureListener);
    }

    public CardCanvas(Card c, BufferedImage cards)
    {
        super();

        this.card = c;
        this.cards = cards;

        this.setSize(Card.CardWidth, Card.CardHeight);

        CardCanvasMouseHandler mouseHandler = new CardCanvasMouseHandler();
        this.addMouseListener(mouseHandler);

        this.dragSource = DragSource.getDefaultDragSource();
        this.dragGestureListener = new CardCanvasDragGestureListener();
        this.dragSourceListener = new CardCanvasDragSourceListener();

        this.dragSource.createDefaultDragGestureRecognizer(this, this.dragAction, this.dragGestureListener);
    }

    /**
     * Draws card onto canvas
     *
     * @param g <b>Graphics</b>
     */
    public void paint(Graphics g)
    {
        super.paint(g);

        if (this.card != null)
        {
            int x = -1;
            int y = -1;

            if (this.card.getRank() == Card.DummyAceCards)
            {
                y = 492;

                int suitNum = 0;
                for (int s = 0; s < Card.Suits.length && suitNum == 0; s++)
                    if (Card.Suits[s] == card.getSuit())
                        suitNum = s;

                x = Card.CardWidth * suitNum;
            }
            else if (this.card.getRank() == Card.DummyCard)
            {
                x = 395;
                y = 492;
            }
            else if (this.card.getFacing())
            {
                x = Card.CardWidth * (card.getRank() - 1);

                int suitNum = 0;
                for (int s = 0; s < Card.Suits.length && suitNum == 0; s++)
                    if (Card.Suits[s] == card.getSuit())
                        suitNum = s;

                y = Card.CardHeight * suitNum;
            }
            else
            {
                x = 316;
                y = 492;
            }

            if (x != -1 && y != -1)
                g.drawImage(this.cards, 0, 0, Card.CardWidth, Card.CardHeight, x, y, x + Card.CardWidth, y + Card.CardHeight, this);

            if (hintVisible)
            {
                g.setColor(Color.blue);
                g.drawLine(0, 0, 0, this.getHeight() - 1);
                g.drawLine(1, 1, 1, this.getHeight() - 2);
                g.drawLine(2, 2, 2, this.getHeight() - 3);
                g.drawLine(this.getWidth() - 1, 0, this.getWidth() - 1, this.getHeight() - 1);
                g.drawLine(this.getWidth() - 2, 1, this.getWidth() - 2, this.getHeight() - 2);
                g.drawLine(this.getWidth() - 3, 2, this.getWidth() - 3, this.getHeight() - 3);

                g.drawLine(0, 0, this.getWidth() - 1, 0);
                g.drawLine(1, 1, this.getWidth() - 2, 1);
                g.drawLine(2, 2, this.getWidth() - 3, 2);
                g.drawLine(0, this.getHeight() - 1, this.getWidth() - 1, this.getHeight() - 1);
                g.drawLine(1, this.getHeight() - 2, this.getWidth() - 2, this.getHeight() - 2);
                g.drawLine(2, this.getHeight() - 3, this.getWidth() - 3, this.getHeight() - 3);
            }
        }
    }

    /**
     * Inserts card into <i>card</i> to be worked with.
     *
     * @param card <b>Card</b>
     * @return card
     */
    public Card setCard(Card card)
    {
        this.card = card;

        return card;
    }

    /**
     * Returns current card in use.
     *
     * @return card
     */
    public Card getCard()
    {
        return card;
    }

    /**
     * Shows a blue border around this card
     */
    public void showHint()
    {
        this.hintVisible = true;

        this.repaint();
    }

    /**
     * Removes borders from this card
     */
    public void hideHint()
    {
        this.hintVisible = false;

        this.repaint();
    }

    /**
     * class to handle the mouse options
     */
    class CardCanvasMouseHandler extends MouseAdapter
    {
        /**
         * Checks for mouse click and then performs an action based on the facing of the card.
         *
         * @param e <i>MouseEvent</i>
         */
        public void mouseClicked(MouseEvent e)
        {
            if (e.getButton() == e.BUTTON1)
            {
                if (CardCanvas.this == CardCanvas.this.pile.pileCards.lastElement() && !CardCanvas.this.card.getFacing())
                {
                    CardCanvas.this.card.setFacing(true);
                    CardCanvas.this.repaint();
                }

                CardCanvas.this.pile.cardClicked(CardCanvas.this == CardCanvas.this.pile.pileCards.lastElement());
            }
            else if (e.getButton() == e.BUTTON3)
            {
                if (pile instanceof KlondikeSuitPile)
                {
                    KlondikeSuitPile ksp = (KlondikeSuitPile)pile;
                    ksp.autoMoveToSuit();
                }
                else if (pile instanceof KlondikeDonePile)
                {
                    KlondikeDonePile kdp = (KlondikeDonePile)pile;
                    kdp.autoMoveToSuit();
                }
                else if (pile instanceof KlondikePlayPile)
                {
                    KlondikePlayPile kpp = (KlondikePlayPile)pile;
                    kpp.autoMoveToSuit();
                }
                else if (pile instanceof FreecellCellPile)
                {
                    FreecellCellPile fcp = (FreecellCellPile)pile;
                    fcp.autoMoveToSuit();
                }
                else if (pile instanceof FreecellFoundationPile)
                {
                    FreecellFoundationPile ffp = (FreecellFoundationPile)pile;
                    ffp.autoMoveToSuit();
                }
                else if (pile instanceof FreecellTableuPile)
                {
                    FreecellTableuPile ftp = (FreecellTableuPile)pile;
                    ftp.autoMoveToSuit();
                }
            }
        }
    }

    /**
     * Class to handle drag gestures
     */
    class CardCanvasDragGestureListener implements DragGestureListener
    {
        /**
         * Decides if all the output should be shown
         */
        private final boolean fullOutput = false;
        
        /**
         * Checks for upfacing card, then creates deck containing cards being moved<
         * @param e <i>DragGestureEvent</i>
         */
        public void dragGestureRecognized(DragGestureEvent e)
        {
            if ((e.getDragAction() & CardCanvas.this.dragAction) == 0 || !CardCanvas.this.card.getFacing())
                return;

            Deck deck = new Deck();
            for (int i = CardCanvas.this.pile.pileCards.indexOf(CardCanvas.this); i < CardCanvas.this.pile.pileCards.size(); i++)
                deck.addCard(CardCanvas.this.pile.pileCards.get(i).getCard());

            Transferable transferable = new DeckTransferable(deck);

            if (fullOutput)
                System.out.println("Drag started");
            try
            {
                e.startDrag(DragSource.DefaultCopyNoDrop, transferable, CardCanvas.this.dragSourceListener);
            }
            catch (InvalidDnDOperationException exc)
            {
                System.err.println(exc);
            }
        }
    }

    /**
     * Class creating actions to the drag deck
     */
    class CardCanvasDragSourceListener implements DragSourceListener
    {
        /**
         * Decides if all the output should be shown
         */
        private final boolean fullOutput = false;
        
        /**
         * Removes card from original pile
         *
         * @param e <i>DragSourceDropEvent</i>
         */
        public void dragDropEnd(DragSourceDropEvent e)
        {
            if (e.getDropSuccess() == false)
            {
                if (fullOutput)
                    System.out.println("Drag not successful :(, CardCanvasDragSourceListener.dragDropEnd()");
                
                return;
            }

            if (e.getDropAction() == DnDConstants.ACTION_MOVE)
            {
                CardCanvas.this.pile.remove(CardCanvas.this);
            }
        }

        /**
         * Checks if move is allowed, then copies card to new location.
         *
         * @param e <i>DragSourceDropEvent</i>
         */
        public void dragEnter(DragSourceDragEvent e)
        {
            DragSourceContext context = e.getDragSourceContext();
            int myaction = e.getDropAction();
            if ((myaction & CardCanvas.this.dragAction) != 0 && CardCanvas.this.card.getFacing())
                context.setCursor(DragSource.DefaultCopyDrop);
            else
                context.setCursor(DragSource.DefaultCopyNoDrop);
        }

        public void dragOver(DragSourceDragEvent e)
        {
            //
        }

        public void dragExit(DragSourceEvent e)
        {
            DragSourceContext context = e.getDragSourceContext();
            context.setCursor(DragSource.DefaultCopyNoDrop);
        }

        /**
         * Changes the appearance of the mouse over legal and illegal moves
         *
         * @param e <i>DragSourceDropEvent</i>
         */
        public void dropActionChanged(DragSourceDragEvent e)
        {
            DragSourceContext context = e.getDragSourceContext();
            context.setCursor(DragSource.DefaultCopyNoDrop);
        }
    }
}
